import sys
import os
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QPushButton, QFileDialog, QSlider, QSpinBox, QComboBox,
                             QTabWidget, QGroupBox, QGridLayout, QAction, QMenuBar, QToolBar,
                             QStatusBar, QMessageBox, QSplitter, QDockWidget, QListWidget)
from PyQt5.QtCore import Qt, QTimer, QPoint, QRect
from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QFont


class CVImageProcessor(QMainWindow):
    """计算机视觉图像处理主窗口"""

    def __init__(self):
        super().__init__()

        # 初始化变量
        self.current_image = None
        self.original_image = None
        self.processed_image = None
        self.is_playing = False
        self.video_capture = None
        self.video_timer = QTimer(self)
        self.crop_rect = None
        self.is_cropping = False
        self.crop_start = None

        # 设置窗口
        self.setWindowTitle("计算机视觉图像处理平台")
        self.setGeometry(100, 100, 1200, 800)

        # 初始化UI
        self._init_ui()

        # 连接信号和槽
        self._connect_signals()

        # 显示欢迎界面
        self._show_welcome_message()

    def _init_ui(self):
        """初始化用户界面"""
        # 创建中央部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QHBoxLayout(central_widget)

        # 创建左侧面板（图像/视频显示区域）
        left_panel = QWidget()
        left_layout = QVBoxLayout(left_panel)

        # 图像/视频显示区域
        self.display_label = QLabel("请加载图像或视频")
        self.display_label.setAlignment(Qt.AlignCenter)
        self.display_label.setMinimumSize(640, 480)
        self.display_label.setStyleSheet("border: 1px solid #cccccc;")
        left_layout.addWidget(self.display_label)

        # 图像/视频控制区域
        control_widget = QWidget()
        control_layout = QHBoxLayout(control_widget)

        self.play_button = QPushButton("播放")
        self.pause_button = QPushButton("暂停")
        self.stop_button = QPushButton("停止")
        self.prev_button = QPushButton("上一帧")
        self.next_button = QPushButton("下一帧")

        self.play_button.setEnabled(False)
        self.pause_button.setEnabled(False)
        self.stop_button.setEnabled(False)
        self.prev_button.setEnabled(False)
        self.next_button.setEnabled(False)

        control_layout.addWidget(self.play_button)
        control_layout.addWidget(self.pause_button)
        control_layout.addWidget(self.stop_button)
        control_layout.addWidget(self.prev_button)
        control_layout.addWidget(self.next_button)

        left_layout.addWidget(control_widget)

        # 创建右侧面板（参数设置和结果显示）
        right_panel = QTabWidget()

        # 参数设置选项卡
        params_widget = QWidget()
        params_layout = QVBoxLayout(params_widget)

        # 图像处理参数组
        image_processing_group = QGroupBox("图像处理参数")
        image_processing_layout = QGridLayout(image_processing_group)

        # 亮度调整
        self.brightness_label = QLabel("亮度:")
        self.brightness_slider = QSlider(Qt.Horizontal)
        self.brightness_slider.setRange(-100, 100)
        self.brightness_slider.setValue(0)

        # 对比度调整
        self.contrast_label = QLabel("对比度:")
        self.contrast_slider = QSlider(Qt.Horizontal)
        self.contrast_slider.setRange(-100, 100)
        self.contrast_slider.setValue(0)

        # 饱和度调整
        self.saturation_label = QLabel("饱和度:")
        self.saturation_slider = QSlider(Qt.Horizontal)
        self.saturation_slider.setRange(-100, 100)
        self.saturation_slider.setValue(0)

        # 平滑处理强度
        self.smoothing_label = QLabel("平滑强度:")
        self.smoothing_slider = QSlider(Qt.Horizontal)
        self.smoothing_slider.setRange(1, 15)
        self.smoothing_slider.setValue(3)
        self.smoothing_slider.setSingleStep(2)

        # 形状识别阈值
        self.shape_threshold_label = QLabel("形状识别阈值:")
        self.shape_threshold_spinbox = QSpinBox()
        self.shape_threshold_spinbox.setRange(50, 200)
        self.shape_threshold_spinbox.setValue(100)

        # 添加到布局
        image_processing_layout.addWidget(self.brightness_label, 0, 0)
        image_processing_layout.addWidget(self.brightness_slider, 0, 1)
        image_processing_layout.addWidget(self.contrast_label, 1, 0)
        image_processing_layout.addWidget(self.contrast_slider, 1, 1)
        image_processing_layout.addWidget(self.saturation_label, 2, 0)
        image_processing_layout.addWidget(self.saturation_slider, 2, 1)
        image_processing_layout.addWidget(self.smoothing_label, 3, 0)
        image_processing_layout.addWidget(self.smoothing_slider, 3, 1)
        image_processing_layout.addWidget(self.shape_threshold_label, 4, 0)
        image_processing_layout.addWidget(self.shape_threshold_spinbox, 4, 1)

        params_layout.addWidget(image_processing_group)

        # 添加到选项卡
        right_panel.addTab(params_widget, "参数设置")

        # 结果显示选项卡
        results_widget = QWidget()
        results_layout = QVBoxLayout(results_widget)

        self.results_text = QLabel("处理结果将显示在这里")
        self.results_text.setWordWrap(True)
        self.results_text.setAlignment(Qt.AlignTop)

        self.results_image = QLabel()
        self.results_image.setAlignment(Qt.AlignCenter)
        self.results_image.setMinimumSize(320, 240)
        self.results_image.setStyleSheet("border: 1px solid #cccccc;")

        results_layout.addWidget(self.results_text)
        results_layout.addWidget(self.results_image)

        right_panel.addTab(results_widget, "结果显示")

        # 添加到主布局
        main_layout.addWidget(left_panel, 7)  # 左侧占70%
        main_layout.addWidget(right_panel, 3)  # 右侧占30%

        # 创建菜单栏
        self._create_menu_bar()

        # 创建工具栏
        self._create_tool_bar()

        # 创建状态栏
        self.statusBar().showMessage("就绪")

    def _create_menu_bar(self):
        """创建菜单栏"""
        menu_bar = self.menuBar()

        # 文件菜单
        file_menu = menu_bar.addMenu("文件")

        open_image_action = QAction("打开图像", self)
        open_image_action.setShortcut("Ctrl+I")
        open_image_action.triggered.connect(self._open_image)

        open_video_action = QAction("打开视频", self)
        open_video_action.setShortcut("Ctrl+V")
        open_video_action.triggered.connect(self._open_video)

        open_camera_action = QAction("打开摄像头", self)
        open_camera_action.setShortcut("Ctrl+C")
        open_camera_action.triggered.connect(self._open_camera)

        save_image_action = QAction("保存图像", self)
        save_image_action.setShortcut("Ctrl+S")
        save_image_action.triggered.connect(self._save_image)

        exit_action = QAction("退出", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)

        file_menu.addAction(open_image_action)
        file_menu.addAction(open_video_action)
        file_menu.addAction(open_camera_action)
        file_menu.addSeparator()
        file_menu.addAction(save_image_action)
        file_menu.addSeparator()
        file_menu.addAction(exit_action)

        # 编辑菜单
        edit_menu = menu_bar.addMenu("编辑")

        rotate_left_action = QAction("向左旋转", self)
        rotate_left_action.setShortcut("Ctrl+Left")
        rotate_left_action.triggered.connect(lambda: self._rotate_image(-90))

        rotate_right_action = QAction("向右旋转", self)
        rotate_right_action.setShortcut("Ctrl+Right")
        rotate_right_action.triggered.connect(lambda: self._rotate_image(90))

        flip_horizontal_action = QAction("水平翻转", self)
        flip_horizontal_action.triggered.connect(lambda: self._flip_image(1))

        flip_vertical_action = QAction("垂直翻转", self)
        flip_vertical_action.triggered.connect(lambda: self._flip_image(0))

        crop_action = QAction("裁剪", self)
        crop_action.setShortcut("Ctrl+X")
        crop_action.triggered.connect(self._start_crop)

        edit_menu.addAction(rotate_left_action)
        edit_menu.addAction(rotate_right_action)
        edit_menu.addSeparator()
        edit_menu.addAction(flip_horizontal_action)
        edit_menu.addAction(flip_vertical_action)
        edit_menu.addSeparator()
        edit_menu.addAction(crop_action)

        # 处理菜单
        process_menu = menu_bar.addMenu("处理")

        adjust_color_action = QAction("调整色彩", self)
        adjust_color_action.triggered.connect(self._adjust_color)

        smooth_image_action = QAction("平滑处理", self)
        smooth_image_action.triggered.connect(self._smooth_image)

        detect_shapes_action = QAction("形状识别", self)
        detect_shapes_action.triggered.connect(self._detect_shapes)

        detect_faces_action = QAction("人脸检测", self)
        detect_faces_action.triggered.connect(self._detect_faces)

        stitch_images_action = QAction("图像拼接", self)
        stitch_images_action.triggered.connect(self._stitch_images)

        process_menu.addAction(adjust_color_action)
        process_menu.addAction(smooth_image_action)
        process_menu.addAction(detect_shapes_action)
        process_menu.addAction(detect_faces_action)
        process_menu.addAction(stitch_images_action)

        # 帮助菜单
        help_menu = menu_bar.addMenu("帮助")

        about_action = QAction("关于", self)
        about_action.triggered.connect(self._show_about)

        help_menu.addAction(about_action)

    def _create_tool_bar(self):
        """创建工具栏"""
        tool_bar = QToolBar("工具栏")
        self.addToolBar(tool_bar)

        # 添加工具按钮
        open_image_action = QAction("打开图像", self)
        open_image_action.triggered.connect(self._open_image)
        tool_bar.addAction(open_image_action)

        open_video_action = QAction("打开视频", self)
        open_video_action.triggered.connect(self._open_video)
        tool_bar.addAction(open_video_action)

        open_camera_action = QAction("打开摄像头", self)
        open_camera_action.triggered.connect(self._open_camera)
        tool_bar.addAction(open_camera_action)

        tool_bar.addSeparator()

        save_image_action = QAction("保存图像", self)
        save_image_action.triggered.connect(self._save_image)
        tool_bar.addAction(save_image_action)

        tool_bar.addSeparator()

        rotate_left_action = QAction("向左旋转", self)
        rotate_left_action.triggered.connect(lambda: self._rotate_image(-90))
        tool_bar.addAction(rotate_left_action)

        rotate_right_action = QAction("向右旋转", self)
        rotate_right_action.triggered.connect(lambda: self._rotate_image(90))
        tool_bar.addAction(rotate_right_action)

        tool_bar.addSeparator()

        detect_faces_action = QAction("人脸检测", self)
        detect_faces_action.triggered.connect(self._detect_faces)
        tool_bar.addAction(detect_faces_action)

    def _connect_signals(self):
        """连接信号和槽"""
        # 视频控制按钮
        self.play_button.clicked.connect(self._play_video)
        self.pause_button.clicked.connect(self._pause_video)
        self.stop_button.clicked.connect(self._stop_video)
        self.prev_button.clicked.connect(self._prev_frame)
        self.next_button.clicked.connect(self._next_frame)

        # 视频定时器
        self.video_timer.timeout.connect(self._update_frame)

        # 参数滑块
        self.brightness_slider.valueChanged.connect(self._adjust_color)
        self.contrast_slider.valueChanged.connect(self._adjust_color)
        self.saturation_slider.valueChanged.connect(self._adjust_color)
        self.smoothing_slider.valueChanged.connect(self._smooth_image)
        self.shape_threshold_spinbox.valueChanged.connect(self._detect_shapes)

    def _show_welcome_message(self):
        """显示欢迎信息"""
        self.statusBar().showMessage("欢迎使用计算机视觉图像处理平台")

    def _open_image(self):
        """打开图像文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif)"
        )

        if file_path:
            try:
                self.current_image = cv2.imread(file_path)
                self.original_image = self.current_image.copy()
                self._display_image(self.current_image)
                self.statusBar().showMessage(f"已加载图像: {os.path.basename(file_path)}")

                # 更新按钮状态
                self.play_button.setEnabled(False)
                self.pause_button.setEnabled(False)
                self.stop_button.setEnabled(False)
                self.prev_button.setEnabled(False)
                self.next_button.setEnabled(False)

                # 重置裁剪状态
                self.is_cropping = False
                self.crop_rect = None

                # 更新结果显示
                self.results_text.setText("图像已加载")
                self._display_result_image(self.current_image)

            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法加载图像: {str(e)}")
                self.statusBar().showMessage("加载图像失败")

    def _open_video(self):
        """打开视频文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开视频", "", "视频文件 (*.mp4 *.avi *.mov *.mkv)"
        )

        if file_path:
            try:
                self.video_capture = cv2.VideoCapture(file_path)

                if not self.video_capture.isOpened():
                    QMessageBox.critical(self, "错误", "无法打开视频文件")
                    self.statusBar().showMessage("打开视频失败")
                    return

                # 读取第一帧
                ret, frame = self.video_capture.read()
                if ret:
                    self.current_image = frame
                    self._display_image(self.current_image)
                    self.statusBar().showMessage(f"已加载视频: {os.path.basename(file_path)}")

                    # 更新按钮状态
                    self.play_button.setEnabled(True)
                    self.pause_button.setEnabled(False)
                    self.stop_button.setEnabled(True)
                    self.prev_button.setEnabled(True)
                    self.next_button.setEnabled(True)

                    # 更新结果显示
                    self.results_text.setText("视频已加载，点击播放按钮开始")
                    self._display_result_image(self.current_image)
                else:
                    QMessageBox.critical(self, "错误", "无法读取视频帧")
                    self.statusBar().showMessage("读取视频帧失败")

            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法加载视频: {str(e)}")
                self.statusBar().showMessage("加载视频失败")

    def _open_camera(self):
        """打开摄像头"""
        try:
            self.video_capture = cv2.VideoCapture(0)  # 0表示默认摄像头

            if not self.video_capture.isOpened():
                QMessageBox.critical(self, "错误", "无法打开摄像头")
                self.statusBar().showMessage("打开摄像头失败")
                return

            # 更新按钮状态
            self.play_button.setEnabled(True)
            self.pause_button.setEnabled(False)
            self.stop_button.setEnabled(True)
            self.prev_button.setEnabled(False)
            self.next_button.setEnabled(False)

            # 开始捕获视频
            self._play_video()
            self.statusBar().showMessage("已连接摄像头")

            # 更新结果显示
            self.results_text.setText("正在从摄像头捕获视频")

        except Exception as e:
            QMessageBox.critical(self, "错误", f"无法打开摄像头: {str(e)}")
            self.statusBar().showMessage("打开摄像头失败")

    def _save_image(self):
        """保存当前图像"""
        if self.current_image is None:
            QMessageBox.warning(self, "警告", "没有可保存的图像")
            return

        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存图像", "", "PNG文件 (*.png);;JPEG文件 (*.jpg);;BMP文件 (*.bmp)"
        )

        if file_path:
            try:
                # 转换颜色空间（OpenCV使用BGR，而保存为RGB）
                if len(self.current_image.shape) == 3:
                    image_to_save = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2RGB)
                else:
                    image_to_save = self.current_image

                cv2.imwrite(file_path, image_to_save)
                self.statusBar().showMessage(f"图像已保存到: {file_path}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法保存图像: {str(e)}")
                self.statusBar().showMessage("保存图像失败")

    def _play_video(self):
        """播放视频"""
        if self.video_capture is None:
            return

        if not self.is_playing:
            self.is_playing = True
            self.video_timer.start(30)  # 约30fps
            self.play_button.setEnabled(False)
            self.pause_button.setEnabled(True)
            self.statusBar().showMessage("正在播放视频")

    def _pause_video(self):
        """暂停视频播放"""
        if self.is_playing:
            self.is_playing = False
            self.video_timer.stop()
            self.play_button.setEnabled(True)
            self.pause_button.setEnabled(False)
            self.statusBar().showMessage("视频已暂停")

    def _stop_video(self):
        """停止视频播放"""
        if self.video_capture is not None:
            self.is_playing = False
            self.video_timer.stop()
            self.video_capture.release()
            self.video_capture = None

            # 更新按钮状态
            self.play_button.setEnabled(False)
            self.pause_button.setEnabled(False)
            self.stop_button.setEnabled(False)
            self.prev_button.setEnabled(False)
            self.next_button.setEnabled(False)

            self.statusBar().showMessage("视频已停止")

    def _prev_frame(self):
        """显示上一帧"""
        if self.video_capture is None:
            return

        # 获取当前帧位置
        current_pos = self.video_capture.get(cv2.CAP_PROP_POS_FRAMES)

        # 设置到前一帧（如果不是第一帧）
        if current_pos > 1:
            self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, current_pos - 2)
            ret, frame = self.video_capture.read()
            if ret:
                self.current_image = frame
                self._display_image(self.current_image)
                self.statusBar().showMessage(f"显示第 {int(current_pos - 1)} 帧")

    def _next_frame(self):
        """显示下一帧"""
        if self.video_capture is None:
            return

        ret, frame = self.video_capture.read()
        if ret:
            current_pos = self.video_capture.get(cv2.CAP_PROP_POS_FRAMES)
            self.current_image = frame
            self._display_image(self.current_image)
            self.statusBar().showMessage(f"显示第 {int(current_pos)} 帧")

    def _update_frame(self):
        """更新视频帧"""
        if self.video_capture is None:
            self._stop_video()
            return

        ret, frame = self.video_capture.read()
        if ret:
            self.current_image = frame
            self._display_image(self.current_image)
        else:
            # 视频结束
            self._stop_video()
            self.statusBar().showMessage("视频播放完毕")

    def _display_image(self, image):
        """在界面上显示图像"""
        if image is None:
            return

        # 转换颜色空间（OpenCV使用BGR，而Qt使用RGB）
        if len(image.shape) == 3:
            rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        else:
            rgb_image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)

        # 创建QImage
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        q_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)

        # 调整图像大小以适应标签
        pixmap = QPixmap.fromImage(q_image)
        scaled_pixmap = pixmap.scaled(
            self.display_label.width(),
            self.display_label.height(),
            Qt.KeepAspectRatio,
            Qt.SmoothTransformation
        )

        # 显示图像
        self.display_label.setPixmap(scaled_pixmap)

    def _display_result_image(self, image):
        """在结果区域显示图像"""
        if image is None:
            return

        # 转换颜色空间（OpenCV使用BGR，而Qt使用RGB）
        if len(image.shape) == 3:
            rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        else:
            rgb_image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)

        # 创建QImage
        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        q_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)

        # 调整图像大小以适应标签
        pixmap = QPixmap.fromImage(q_image)
        scaled_pixmap = pixmap.scaled(
            self.results_image.width(),
            self.results_image.height(),
            Qt.KeepAspectRatio,
            Qt.SmoothTransformation
        )

        # 显示图像
        self.results_image.setPixmap(scaled_pixmap)

    def _adjust_color(self):
        """调整图像色彩（亮度、对比度、饱和度）"""
        if self.current_image is None:
            return

        self.statusBar().showMessage("正在调整色彩...")

        brightness = self.brightness_slider.value()
        contrast = self.contrast_slider.value()
        saturation = self.saturation_slider.value()

        # 复制原始图像
        img = self.original_image.copy()

        # 亮度调整
        if brightness != 0:
            if brightness > 0:
                shadow = brightness
                highlight = 255
            else:
                shadow = 0
                highlight = 255 + brightness

            alpha_b = (highlight - shadow) / 255
            gamma_b = shadow

            img = cv2.addWeighted(img, alpha_b, img, 0, gamma_b)

        # 对比度调整
        if contrast != 0:
            f = 131 * (contrast + 127) / (127 * (131 - contrast))
            alpha_c = f
            gamma_c = 127 * (1 - f)

            img = cv2.addWeighted(img, alpha_c, img, 0, gamma_c)

        # 饱和度调整（仅对彩色图像有效）
        if saturation != 0 and len(img.shape) == 3:
            hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
            h, s, v = cv2.split(hsv)

            if saturation > 0:
                lim = 255 - saturation
                s[s > lim] = 255
                s[s <= lim] += saturation
            else:
                s = s + saturation
                s = np.clip(s, 0, 255)

            hsv = cv2.merge((h, s, v))
            img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

        self.current_image = img
        self._display_image(self.current_image)

        # 更新结果显示
        self.results_text.setText(f"亮度: {brightness}, 对比度: {contrast}, 饱和度: {saturation}")
        self._display_result_image(self.current_image)

        self.statusBar().showMessage("色彩调整完成")

    def _smooth_image(self):
        """平滑处理图像"""
        if self.current_image is None:
            return

        self.statusBar().showMessage("正在进行平滑处理...")

        kernel_size = self.smoothing_slider.value()

        # 确保核大小为奇数
        if kernel_size % 2 == 0:
            kernel_size += 1

        # 应用高斯模糊
        img = cv2.GaussianBlur(self.current_image, (kernel_size, kernel_size), 0)

        self.current_image = img
        self._display_image(self.current_image)

        # 更新结果显示
        self.results_text.setText(f"平滑处理完成，核大小: {kernel_size}x{kernel_size}")
        self._display_result_image(self.current_image)

        self.statusBar().showMessage("平滑处理完成")

    def _detect_shapes(self):
        """检测图像中的形状"""
        if self.current_image is None:
            return

        self.statusBar().showMessage("正在检测形状...")

        threshold = self.shape_threshold_spinbox.value()

        # 复制当前图像
        img = self.current_image.copy()

        # 转换为灰度图
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # 模糊处理
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)

        # 边缘检测
        edged = cv2.Canny(blurred, 50, threshold)

        # 查找轮廓
        contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # 在原图上绘制轮廓
        output = img.copy()
        shapes_info = []

        for contour in contours:
            # 计算轮廓面积
            area = cv2.contourArea(contour)

            # 忽略小面积的轮廓
            if area < 100:
                continue

            # 近似轮廓
            epsilon = 0.04 * cv2.arcLength(contour, True)
            approx = cv2.approxPolyDP(contour, epsilon, True)

            # 获取轮廓的边界框
            x, y, w, h = cv2.boundingRect(approx)

            # 确定形状
            shape = "未识别"

            if len(approx) == 3:
                shape = "三角形"
            elif len(approx) == 4:
                # 计算宽高比
                aspect_ratio = float(w) / h

                # 根据宽高比判断是矩形还是正方形
                if aspect_ratio >= 0.95 and aspect_ratio <= 1.05:
                    shape = "正方形"
                else:
                    shape = "矩形"
            elif len(approx) > 4:
                # 计算轮廓面积与边界圆面积的比率
                ((x_c, y_c), radius) = cv2.minEnclosingCircle(approx)
                circle_area = np.pi * radius * radius
                ratio = area / circle_area

                # 根据比率判断是圆形还是多边形
                if ratio > 0.8:
                    shape = "圆形"
                else:
                    shape = f"多边形({len(approx)}边)"

            # 在图像上绘制轮廓和形状名称
            cv2.drawContours(output, [contour], -1, (0, 255, 0), 2)
            cv2.putText(output, shape, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

            # 记录形状信息
            shapes_info.append(f"{shape} (面积: {int(area)}像素)")

        self.current_image = output
        self._display_image(self.current_image)

        # 更新结果显示
        if shapes_info:
            shapes_text = "\n".join(shapes_info)
            self.results_text.setText(f"检测到的形状:\n{shapes_text}")
        else:
            self.results_text.setText("未检测到形状")

        self._display_result_image(self.current_image)

        self.statusBar().showMessage(f"形状检测完成，共检测到 {len(shapes_info)} 个形状")

    def _detect_faces(self):
        """检测图像中的人脸"""
        if self.current_image is None:
            return

        self.statusBar().showMessage("正在检测人脸...")

        # 复制当前图像
        img = self.current_image.copy()

        # 转换为灰度图
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # 加载人脸检测器
        face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

        # 检测人脸
        faces = face_cascade.detectMultiScale(gray, 1.3, 5)

        # 在原图上绘制人脸矩形
        for (x, y, w, h) in faces:
            cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)

        self.current_image = img
        self._display_image(self.current_image)

        # 更新结果显示
        if len(faces) > 0:
            self.results_text.setText(f"检测到 {len(faces)} 个人脸")
        else:
            self.results_text.setText("未检测到人脸")

        self._display_result_image(self.current_image)

        self.statusBar().showMessage(f"人脸检测完成，共检测到 {len(faces)} 个人脸")

    def _stitch_images(self):
        """图像拼接"""
        if self.current_image is None:
            QMessageBox.warning(self, "警告", "请先加载至少一张图像")
            return

        # 选择要拼接的其他图像
        file_paths, _ = QFileDialog.getOpenFileNames(
            self, "选择要拼接的图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp)"
        )

        if not file_paths:
            return

        self.statusBar().showMessage("正在进行图像拼接...")

        # 创建图像拼接器
        stitcher = cv2.Stitcher_create() if hasattr(cv2, 'Stitcher') else cv2.Stitcher_create()

        # 收集所有要拼接的图像
        images = [self.current_image]
        for file_path in file_paths:
            img = cv2.imread(file_path)
            if img is not None:
                images.append(img)

        # 进行拼接
        status, stitched = stitcher.stitch(images)

        if status == cv2.Stitcher_OK:
            self.current_image = stitched
            self._display_image(self.current_image)

            # 更新结果显示
            self.results_text.setText(f"图像拼接完成，共拼接 {len(images)} 张图像")
            self._display_result_image(self.current_image)

            self.statusBar().showMessage(f"图像拼接完成，共拼接 {len(images)} 张图像")
        else:
            QMessageBox.critical(self, "错误", f"图像拼接失败，错误代码: {status}")
            self.statusBar().showMessage("图像拼接失败")

    def _rotate_image(self, angle):
        """旋转图像"""
        if self.current_image is None:
            return

        self.statusBar().showMessage(f"正在旋转图像 {angle} 度...")

        # 获取图像尺寸和中心点
        (h, w) = self.current_image.shape[:2]
        center = (w // 2, h // 2)

        # 计算旋转矩阵
        M = cv2.getRotationMatrix2D(center, angle, 1.0)

        # 执行旋转
        rotated = cv2.warpAffine(self.current_image, M, (w, h))

        self.current_image = rotated
        self._display_image(self.current_image)

        # 更新结果显示
        self.results_text.setText(f"图像已旋转 {angle} 度")
        self._display_result_image(self.current_image)

        self.statusBar().showMessage(f"图像旋转完成")

    def _flip_image(self, direction):
        """翻转图像"""
        if self.current_image is None:
            return

        self.statusBar().showMessage("正在翻转图像...")

        # 0表示垂直翻转，1表示水平翻转
        flipped = cv2.flip(self.current_image, direction)

        self.current_image = flipped
        self._display_image(self.current_image)

        # 更新结果显示
        if direction == 0:
            flip_text = "垂直"
        else:
            flip_text = "水平"

        self.results_text.setText(f"图像已{flip_text}翻转")
        self._display_result_image(self.current_image)

        self.statusBar().showMessage(f"图像翻转完成")

    def _start_crop(self):
        """开始裁剪图像"""
        if self.current_image is None:
            return

        if self.is_cropping:
            # 如果已经在裁剪模式，执行裁剪
            self._perform_crop()
        else:
            # 进入裁剪模式
            self.is_cropping = True
            self.crop_rect = None
            self.crop_start = None
            self.statusBar().showMessage("请在图像上拖动鼠标选择裁剪区域，选好后再次点击裁剪按钮确认")

    def _perform_crop(self):
        """执行裁剪"""
        if self.crop_rect is None or self.current_image is None:
            self.is_cropping = False
            self.statusBar().showMessage("未选择裁剪区域")
            return

        # 获取裁剪区域
        x, y, w, h = self.crop_rect

        # 执行裁剪
        cropped = self.current_image[y:y + h, x:x + w]

        if cropped.size == 0:
            self.is_cropping = False
            self.statusBar().showMessage("裁剪区域无效")
            return

        self.current_image = cropped
        self._display_image(self.current_image)

        # 更新结果显示
        self.results_text.setText(f"图像已裁剪，尺寸: {w}x{h}")
        self._display_result_image(self.current_image)

        # 退出裁剪模式
        self.is_cropping = False
        self.statusBar().showMessage("图像裁剪完成")

    def mousePressEvent(self, event):
        """鼠标按下事件"""
        if self.is_cropping and event.button() == Qt.LeftButton:
            self.crop_start = event.pos()
            self.crop_rect = None

    def mouseMoveEvent(self, event):
        """鼠标移动事件"""
        if self.is_cropping and self.crop_start is not None and event.buttons() & Qt.LeftButton:
            # 获取当前鼠标位置
            current_pos = event.pos()

            # 计算矩形区域
            x = min(self.crop_start.x(), current_pos.x())
            y = min(self.crop_start.y(), current_pos.y())
            w = abs(current_pos.x() - self.crop_start.x())
            h = abs(current_pos.y() - self.crop_start.y())

            self.crop_rect = (x, y, w, h)

            # 重新显示图像并绘制裁剪矩形
            self._display_image(self.current_image)
            self._draw_crop_rect()

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        if self.is_cropping and event.button() == Qt.LeftButton and self.crop_start is not None:
            # 确保裁剪矩形有效
            if self.crop_rect and (self.crop_rect[2] > 0 and self.crop_rect[3] > 0):
                self.statusBar().showMessage("裁剪区域已选择，点击裁剪按钮确认")
            else:
                self.crop_rect = None
                self.statusBar().showMessage("无效的裁剪区域，请重新选择")

    def _draw_crop_rect(self):
        """在图像上绘制裁剪矩形"""
        if self.crop_rect is None:
            return

        # 获取当前显示的图像
        pixmap = self.display_label.pixmap()
        if pixmap is None:
            return

        # 创建绘图对象
        painter = QPainter(pixmap)
        painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))

        # 绘制矩形
        x, y, w, h = self.crop_rect
        painter.drawRect(x, y, w, h)

        # 添加文本说明
        font = QFont()
        font.setPointSize(10)
        painter.setFont(font)
        painter.drawText(x, y - 5, f"{w}x{h}")

        painter.end()

        # 重新显示图像
        self.display_label.setPixmap(pixmap)

    def _show_about(self):
        """显示关于对话框"""
        QMessageBox.about(
            self,
            "关于",
            "计算机视觉图像处理平台\n\n"
            "版本: 1.0\n"
            "作者: Doubao\n\n"
            "本平台提供图像处理和计算机视觉功能，包括：\n"
            "- 图像加载、显示和保存\n"
            "- 基本图像处理（旋转、翻转、裁剪等）\n"
            "- 色彩调整（亮度、对比度、饱和度）\n"
            "- 图像平滑处理\n"
            "- 形状识别\n"
            "- 人脸检测\n"
            "- 图像拼接\n"
            "- 视频播放与处理"
        )


if __name__ == "__main__":
    # 确保中文显示正常
    import matplotlib

    matplotlib.use('Agg')

    app = QApplication(sys.argv)
    window = CVImageProcessor()
    sys.exit(app.exec_())